package com.wt.admin.service.sys.impl;

import cn.hutool.core.date.DateField;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjectUtil;
import com.wt.admin.code.sys.SysSettingCode;
import com.wt.admin.code.sys.SysUserCode;
import com.wt.admin.config.ConstVar;
import com.wt.admin.config.cache.Cache;
import com.wt.admin.domain.dto.sys.SysUserInfoUpdateDTO;
import com.wt.admin.domain.dto.sys.SysUserLoginDTO;
import com.wt.admin.domain.dto.sys.SysUserPasswordDTO;
import com.wt.admin.domain.entity.sys.SysSettingEntity;
import com.wt.admin.domain.entity.sys.SysUserEntity;
import com.wt.admin.domain.vo.sys.UserVO;
import com.wt.admin.service.sys.SysProxyService;
import com.wt.admin.service.sys.SysSettingService;
import com.wt.admin.service.sys.SysUserService;
import com.wt.admin.util.AssertUtil;
import jakarta.annotation.Resource;
import lombok.Data;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;

@Service
public class SysProxyServiceImpl implements SysProxyService {

    @Resource
    private SysSettingService sysSettingService;
    @Resource
    private SysUserService sysUserService;
    @Resource
    private Cache<String, SysSettingEntity> settingCache;
    private final Map<String,ErrorMap> errorCount = new HashMap<>(10);


    @Override
    public SysUserEntity userEdit(SysUserEntity data) {
        SysSettingEntity sysSettingEntity = settingCache.get(ConstVar.SettingModel.SETTING);
        AssertUtil.Boolean.isTrue(ObjectUtil.isEmpty(sysSettingEntity) ||
                ObjectUtil.isEmpty(sysSettingEntity.getUser()) ||
                ObjectUtil.isEmpty(sysSettingEntity.getUser().getPasswordInit())
        , SysSettingCode.SETTING_CODE_1305);
        AssertUtil.Boolean.isTrue(ObjectUtil.isEmpty(data.getAccount()) || data.getAccount().length() < sysSettingEntity.getUser().getAccountLength()
                , SysUserCode.USER_CODE_1006,sysSettingEntity.getUser().getAccountLength());
        data.setPassword(sysSettingEntity.getUser().getPasswordInit());
        data.setExpiredTime(DateUtil.offset(new Date(), DateField.DAY_OF_MONTH, sysSettingEntity.getUser().getPasswordExpired()));
        return sysUserService.edit(data);
    }

    @Override
    public SysUserEntity userInitPassword(SysUserPasswordDTO data) {
        SysSettingEntity sysSettingEntity = settingCache.get(ConstVar.SettingModel.SETTING);
        AssertUtil.Boolean.isTrue(ObjectUtil.isEmpty(sysSettingEntity) ||
                        ObjectUtil.isEmpty(sysSettingEntity.getUser()) ||
                        ObjectUtil.isEmpty(sysSettingEntity.getUser().getPasswordInit())
                , SysSettingCode.SETTING_CODE_1305);
        data.setPassword(sysSettingEntity.getUser().getPasswordInit());
        data.setOkPassword(sysSettingEntity.getUser().getPasswordInit());
        data.setExpiredTime(DateUtil.offset(new Date(), DateField.DAY_OF_MONTH, sysSettingEntity.getUser().getPasswordExpired()));
        return sysUserService.updatePassword(data);
    }

    @Override
    public UserVO login(SysUserLoginDTO data) {
        String password = data.getPassword();
        SysSettingEntity sysSettingEntity = settingCache.get(ConstVar.SettingModel.SETTING);
        UserVO login;
        Integer orDefault = errorCount.getOrDefault(data.getClientIP(), new ErrorMap(sysSettingEntity.getUser().getClosedTime())).getCount();
        AssertUtil.Boolean.isTrue(orDefault >= sysSettingEntity.getUser().getErrorCount(),SysUserCode.USER_CODE_1011,
                sysSettingEntity.getUser().getErrorCount() ,sysSettingEntity.getUser().getClosedTime());
        try {
            data.setTokenExpired(sysSettingEntity.getUser().getTokenExpired());
            login = sysUserService.login(data);
            errorCount.remove(data.getClientIP());
        }catch (Exception e) {
            Integer compute = errorCount.compute(data.getClientIP(), (k, v) -> v == null ? new ErrorMap(sysSettingEntity.getUser().getClosedTime()) : v.add())
                    .getCount();
            AssertUtil.Boolean.isTrue(compute >= sysSettingEntity.getUser().getErrorCount(),SysUserCode.USER_CODE_1011,
                    sysSettingEntity.getUser().getErrorCount() ,sysSettingEntity.getUser().getClosedTime());
            throw e;
        }
        Boolean forcedModification = sysSettingEntity.getUser().getForcedModification();
        login.setUpdate(forcedModification && sysSettingEntity.getUser().getPasswordInit().equals(password));
        return login;
    }

    @Override
    public SysUserEntity userUpdateInfo(SysUserInfoUpdateDTO data, String token) {
        SysSettingEntity sysSettingEntity = settingCache.get(ConstVar.SettingModel.SETTING);
        AssertUtil.Boolean.isTrue(ObjectUtil.isEmpty(sysSettingEntity) ||
                        ObjectUtil.isEmpty(sysSettingEntity.getUser()) ||
                        ObjectUtil.isEmpty(sysSettingEntity.getUser().getPasswordInit())
                , SysSettingCode.SETTING_CODE_1305);
        if(data.getUpdate()){
            AssertUtil.Str.isEmpty(data.getPassword(), SysUserCode.USER_CODE_1004);
            AssertUtil.Str.isEmpty(data.getOkPassword(), SysUserCode.USER_CODE_1004);
            AssertUtil.Str.isEQ(data.getPassword(),data.getOkPassword(), SysUserCode.USER_CODE_1009);
        }
        if(ObjectUtil.isNotEmpty(data.getPassword())){
            data.setExpiredTime(DateUtil.offset(new Date(), DateField.DAY_OF_MONTH, sysSettingEntity.getUser().getPasswordExpired()));
            sysSettingService.check(data.getPassword(), sysSettingEntity.getUser());
        }
        return sysUserService.updateInfo(data,token);
    }

    @Scheduled(cron = "0 0/1 * * * ?")
    public void tokenExpiredTimer(){
        errorCount.entrySet().removeIf(entry -> entry.getValue().isExpired());
    }


    @Override
    public SysSettingService getSysSettingService() {
        return this.sysSettingService;
    }

    @Override
    public SysUserService getSysUserService() {
        return this.sysUserService;
    }

    @Data
    public static class ErrorMap{
        private Integer count = 1;
        private Long time;

        public ErrorMap(Integer duration) {
            this.time = DateUtil.offset(new Date(), DateField.MINUTE,duration).getTime();
        }

        public ErrorMap add(){
            this.count++;
            return this;
        }

        public boolean isExpired() {
            return DateUtil.date().getTime() > this.time;
        }
    }

}
